import math
import string
import re

from collections import Counter

import numpy as np

TypeMappingDict={}

for c in 'AEIOUaeiou':
    TypeMappingDict[c]='a'

for c in string.ascii_letters:
    if c not in TypeMappingDict:
        TypeMappingDict[c]='c'

for c in string.digits:
    if c not in TypeMappingDict:
        TypeMappingDict[c]='0'
        
for c in string.punctuation:
    if c not in TypeMappingDict:
        TypeMappingDict[c]='!'

for c in '\n':
    if c not in TypeMappingDict:
        TypeMappingDict[c]='\n'


def string_entropy(s:str):
    
    s=''.join([TypeMappingDict.get(c,'?') for c in s])
    
    char_counts = Counter(s)
    
    selected_chars=['a','c','0','!','\n']

    select_count=[char_counts[c] for c in selected_chars]

    # 计算字符串长度
    string_length = sum(select_count)

    # 计算每个字符的概率
    char_probabilities = [count / string_length for count in select_count if count>0]

    # 计算熵
    entropy = -sum(prob * math.log2(prob) for prob in char_probabilities)

    return entropy

def normal_entropy(s:str,valid_chars:str=string.printable):
    char_counts = Counter(s)

    # 选择有效字符
    valid_char_count = np.zeros(len(valid_chars), dtype=np.float32)
    for i,c in enumerate(valid_chars):
        valid_char_count[i]=char_counts.get(c, 0)

    # 计算字符串长度
    string_length = np.sum(valid_char_count)
    
    if string_length==0:
        return 0.
    
    char_probabilities=valid_char_count/string_length
    
    char_probabilities=np.where(char_probabilities>0,char_probabilities,1.)
    
    # 使用numpy库计算熵
    entropy = -np.sum(char_probabilities * np.log2(char_probabilities))

    return entropy



def compute_entropy(path):
    with open(path,'r',errors='ignore') as f:
        s=f.read()
        return normal_entropy(s)
    
    
class ObfuscationDetector:
    
    @staticmethod
    def remove_comments(s:str):
        # remove javascript comment
        s=re.sub(r'//.*','',s)
        s=re.sub(r'/\*.*?\*/','',s)
        
        return s
    
    @staticmethod
    def entropy_value(path):
        with open(path,'r',errors='ignore') as f:
            s=f.read()
            
            s=ObfuscationDetector.remove_comments(s)
            
            return normal_entropy(s)
    
    
    @staticmethod
    def entropy(path):
        with open(path,'r',errors='ignore') as f:
            s=f.read()
            
            s=ObfuscationDetector.remove_comments(s)
            
            if len(s)<256: return False
            
            e=normal_entropy(s)
            if e>=5.44855:
                return True
        
        return False